﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;

namespace rehot
{
    using rehot.core;
    using SanlieHash;
    using SanlieHash.Fast;
    using SanlieHash.GuoMi;
    public class HotOneDLL : IHotDLL
    {
        internal Assembly assembly { get; set; }
        internal string dll_path { get; set; }
        internal HotTypeBox typebox { get; set; }
        internal HotResourceBox resourceBox { get; set; }
        internal string lastdllhash { get; set; }
        public HotAttributes Attributes { get; internal set; }
        public string Id { get; internal set; }
        public Assembly SystemAssembly { get { return assembly; } }
        public HotOneDLL(string dll_path)
        {
            this.dll_path = dll_path;
            typebox = new HotTypeBox();
            resourceBox = new HotResourceBox();
            ReLoad();
        }
        public HotOneDLL(byte[] dll_memory)
        {
            typebox = new HotTypeBox();
            resourceBox = new HotResourceBox();
            __LoadAssembly(Assembly.Load(dll_memory));
        }
        public HotOneDLL(Assembly assembly)
        {
            typebox = new HotTypeBox();
            resourceBox = new HotResourceBox();
            __LoadAssembly(assembly);
        }
        /// <summary>
        /// 重新加载
        /// </summary>
        public void ReLoad()
        {
            var dll_buf = File.ReadAllBytes(dll_path);
            var sm3 = new SM3();
            var dllhash = FastEncode.Base64Encode(sm3.MakeBytes(dll_buf));

            if (dllhash != lastdllhash)
            {
                lastdllhash = dllhash;
                __LoadAssembly(Assembly.Load(dll_buf));
            }
        }
        internal void __LoadAssembly(Assembly assembly)
        {
            this.assembly = assembly;
            Id = FastHash.GetSM3("Assembly#" + this.assembly.FullName);
            resourceBox.Clear();
            typebox.Clear();
            // 变量向外部暴露的类型
            foreach (var _type in assembly.GetExportedTypes()) typebox.Add(_type.Name, _type);
            // 内部资源名称
            foreach (var _resName in assembly.GetManifestResourceNames()) resourceBox.Add(_resName);
            this.Attributes = new HotAttributes(this);
        }
        public string FindTypeId(string _name, string _namespace = null)
        {
            return typebox.FindId(_name, _namespace);
        }
        public HotType FindType(string _name, string _namespace = null)
        {
            return typebox.FindHotType(_name, _namespace);
        }
        public HotType FindTypeById(string id)
        {
            return typebox.FindHotTypeById(id);
        }
        public string FindResourceId(string _filename)
        {
            return resourceBox.FindId(_filename);
        }
        public IEnumerable<string> FindResourceIds(string _filename)
        {
            return resourceBox.FindIds(_filename);
        }
        public Stream FindResourceById(string id)
        {
            if (!string.IsNullOrEmpty(id))
            {
                var _resName = resourceBox.FindById(id);
                if (_resName != string.Empty) return assembly.GetManifestResourceStream(_resName);
            }
            return Stream.Null;
        }
        public Stream FindResource(string _filename)
        {
            var id = resourceBox.FindId(_filename);
            return FindResourceById(id);
        }
        public IEnumerable<HotAttribute> FindAttributes()
        {
            foreach (var attr in Attributes.ReadAttributesByParentId(Id)) yield return attr;
        }
        public IEnumerable<string> FindAttributesId()
        {
            foreach (var attr in Attributes.FindAttributesByParentId(Id)) yield return attr;
        }
        public string FindAttributeId(string name)
        {
            return Attributes.FindAttributeId(Id, name);
        }
        public HotAttribute FindAttribute(string name)
        {
            return FindAttributeById(Attributes.FindAttributeId(Id, name));
        }
        public HotAttribute FindAttributeById(string id)
        {
            if (string.IsNullOrEmpty(id)) return null;
            return Attributes.ReadAttributeById(id);
        }
        public IEnumerable<string> FindTypesIdByInterface(string interface_name)
        {
            return typebox.FindTypesByInterface(interface_name);
        }
        public IEnumerable<string> FindTypesIdByAttribute(HotType attr_type)
        {
            return typebox.FindTypesByAttribute(attr_type);
        }
        public void Dispose()
        {
            if (typebox != null) typebox.Lose();
            if (Attributes != null) Attributes.Lose();
            if (resourceBox != null) resourceBox.Clear();
            assembly = null;
            typebox = null;
            resourceBox = null;
            Attributes = null;
        }
    }
}
